import java.util.ArrayList;
import java.util.Arrays;
import java.util.jar.Attributes.Name;

import processing.core.PImage;
import processing.xml.XMLElement;


public class Artist {

	public final String arid; 				 // airid     ->   id="8538e728-ca0b-4321-b7e5-cff6565dd4c0"
	public final String arName;				 // artist   ->  <name>Depeche Mode</name>


	// User breakdown
	public ArrayList<String> user_nationalities = new ArrayList<String>();
	public ArrayList<Integer> user_ages_integers = new ArrayList<Integer>();
	public ArrayList<String> user_ages_intervals = new ArrayList<String>();
	public String user_sex = "";

	public final ArtistInfo additionalInfo;

	//Complete constructor of the class artist
	public Artist(String arid, String arName, String begin,
			String end, String comment, String[] aliases,
			String country, String gender, String[] tags, PImage icon) {
		super();
		this.arid = arid;
		this.arName = arName;
		this.additionalInfo = new ArtistInfo(begin, end,comment, aliases, country, gender, tags, icon) ;

	}

	//Constructor of the class artist when no additional information are known
	public Artist(String arid, String arName) {
		super();
		this.arid = arid;
		this.arName = arName;
		this.additionalInfo = null;
	}

	//Constructor of the class artist with defined info
	public Artist(String arid, String arName, ArtistInfo info) {
		super();
		this.arid = arid;
		this.arName = arName;
		this.additionalInfo = info;
	}


	// Artist MusicBrainz
	public static void addArtist(String arid, String arName) {
		addArtistThread thread = new addArtistThread(arid,arName);
		thread.start();	
	}

	public static void downloadLastfmTopArtists() {
		lastfmTopArtists thread= new lastfmTopArtists();
		thread.start();
	}




	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((arid == null) ? 0 : arid.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		Artist other = (Artist) obj;
		if (arid == null) {
			if (other.arid != null)
				return false;
		} else if (!arid.equals(other.arid))
			return false;
		return true;
	}

	// Calculate distance between 2 artists common users
	public static float calculateArtistDistances(Artist a1, Artist a2){

		final int DISTANCE_SAME_SEX = 0, DISTANCE_DIFFERENT_SEX = 10;
		final int DISTANCE_DIFFERENT_CONTINENT = 10, DISTANCE_SAME_CONTINENT = 5, DISTANCE_SAME_STATE = 0;
		final int DISTANCE_2_AGES = 0, DISTANCE_5_AGES = 5, DISTANCE_MORE_AGES = 10;


		float totalDistance=1000;
		int sexDistance;
		ArrayList<Integer> nationalitiesDistance = new ArrayList<Integer>();
		ArrayList<Integer> agesDistance = new ArrayList<Integer>();


		// Calculate distances between sex of the common user
		if(a1.user_sex.equals(a2.user_sex))
			sexDistance = DISTANCE_SAME_SEX;
		else sexDistance = DISTANCE_DIFFERENT_SEX;

		// Calculate distances between nationalities
		for(int i=0;i<a1.user_nationalities.size();i++){
			boolean sameState=false,sameContinent=false;
			for(int j=0;j<a2.user_nationalities.size();j++){
				if(a1.user_nationalities.get(i).equals(a2.user_nationalities.get(j))){
					sameState = true;
					break;
				}
				if(GLOBAL.map_allNationalities.get(a1.user_nationalities.get(i)).continent.name.equals(
						GLOBAL.map_allNationalities.get(a2.user_nationalities.get(j)).continent.name))
					sameContinent = true;
			}
			int distNationality = 0;
			if(sameState)
				distNationality = DISTANCE_SAME_STATE;
			else if(sameContinent)
				distNationality = DISTANCE_SAME_CONTINENT;
			else distNationality = DISTANCE_DIFFERENT_CONTINENT;
			nationalitiesDistance.add(distNationality);
		}

		// Calculate distances between ages
		for(int i=0;i<a1.user_ages_integers.size();i++){
			boolean lessEq2=false,lessEq5=false;
			for(int j=0;j<a2.user_ages_integers.size();j++){
				int dist = Math.abs(a1.user_ages_integers.get(i)-a2.user_ages_integers.get(j));
				if(dist <=2){		// Integer 2 !!
					lessEq2 = true;
					break;
				}
				else if (dist <=5) 		// Integer 5 !!
					lessEq5 = true;
			}
			int distAges = 0;
			if(lessEq2)
				distAges = DISTANCE_2_AGES;
			else if(lessEq5)
				distAges = DISTANCE_5_AGES;
			else distAges = DISTANCE_MORE_AGES;
			agesDistance.add(distAges);	
		}



		// Average/Min nationalitiesDistance
		float avgNationalityDistance = 0;
		for(Integer val : nationalitiesDistance){
			avgNationalityDistance += val;
		}
		avgNationalityDistance = avgNationalityDistance / nationalitiesDistance.size();

		// Average/Min agesDistance
		float avgAgesDistance = 0;
		for(Integer val : agesDistance){
			avgAgesDistance += val;
		}
		avgAgesDistance = avgAgesDistance / agesDistance.size();


		totalDistance = sexDistance + avgAgesDistance + avgNationalityDistance;
		return totalDistance;

	}


	public static boolean areArtistsAffine(Artist a1, Artist a2){
		final float MAX_AFFINE_DISTANCE = 25;

		float distance = calculateArtistDistances(a1,a2);		
		if(distance< MAX_AFFINE_DISTANCE)
			return true;
		return false;
	}


	// Thread that inserts a new artist
	public static class addArtistThread extends Thread{

		String arid, arName;

		public addArtistThread(String arid, String arName2) {
			this.arid = arid;
			this.arName = arName2;
		}

		public void run() {
			String artistType = "-";
			String url;
			XMLElement xml_mb = null;
			XMLElement xml_lastfm = null;
			String begin = "-";
			String end = "-";
			String comment = "-";
			String[] aliases = new String[1]; aliases[0] = "-";
			String country = "-";
			String gender = "-";
			String[] tags = new String[1]; tags[0] = "-";

			PImage icon = null;

			ArtistInfo additionalInfo = null;
			
			
			// Sum artist active
			GLOBAL.artistsThreadsActive++;
			try{

			if (arid != null && !arid.equals("") && arid.length() > 5) {
				url = "http://musicbrainz.org/ws/2/artist/?query=arid:"+arid;
				xml_mb = new XMLElement(GLOBAL.processing,url);
				//System.out.println(arid);

				if (xml_mb != null) {

					if (xml_mb.getChild("artist-list/artist/name")!=null) {
						arName = xml_mb.getChild("artist-list/artist/name").getContent();
						//System.out.println("Nome "+arName);
					}

					// Get artist type
					if (xml_mb.getChild("artist-list/artist")!=null) {
						artistType = xml_mb.getChild("artist-list/artist").getString("type");
						//						System.out.println(artistType);
					}

					if (xml_mb.getChild("artist-list/artist/life-span/begin")!=null) {
						// String version
						begin = xml_mb.getChild("artist-list/artist/life-span/begin").getContent();
						//						System.out.println(begin);
					}

					if (xml_mb.getChild("artist-list/artist/life-span/end")!=null) {
						// String version
						end = xml_mb.getChild("artist-list/artist/life-span/end").getContent();
						//						System.out.println(end);
					}

					if (xml_mb.getChild("artist-list/artist/gender")!=null) {
						gender = xml_mb.getChild("artist-list/artist/gender").getContent();
						//						System.out.println(gender);
					}			

					if (xml_mb.getChild("artist-list/artist/comment")!=null) {
						comment = xml_mb.getChild("artist-list/artist/comment").getContent();
						//						System.out.println(comment);
					}

					if (xml_mb.getChild("artist-list/artist/country")!=null) {
						country = xml_mb.getChild("artist-list/artist/country").getContent();
						//						System.out.println(country);
					}

					if (xml_mb.getChild("artist-list/artist/alias-list")!=null) {
						XMLElement el = xml_mb.getChild("artist-list/artist/alias-list");
						int count = el.getChildCount();
						aliases = new String[count];
						for (int i=0; i< count; i++){
							XMLElement next = el.getChild(i);
							aliases[i] = next.getContent();
							//							System.out.println("Alias: "+aliases[i]);
						}
					}

					if (xml_mb.getChild("artist-list/artist/tag-list")!=null) {
						XMLElement el = xml_mb.getChild("artist-list/artist/tag-list");
						int count = el.getChildCount();
						tags = new String[count];
						for (int i=0; i< count; i++){
							XMLElement next = el.getChild(i).getChild(0); //tag name
							tags[i] = next.getContent();
							//							System.out.println("Tag: "+tags[i]);
						}
					}

					// Search for the image
					try {
						url = "http://ws.audioscrobbler.com/2.0/?method=artist.getimages&mbid="+arid+"&api_key=43e01402e7c496a4563b540e46c7355a";
						xml_lastfm = new XMLElement(GLOBAL.processing,url);
						if (xml_lastfm.getChild("images/image/sizes")!=null) {
							XMLElement el = xml_lastfm.getChild("images/image/sizes");
							int count = el.getChildCount();
							for (int i=0; i<count; i++) {
								XMLElement next = el.getChild(i);
								if (next.getString("name").equalsIgnoreCase("small")) { //small or largesquare or medium
									//System.out.println("Image: "+next.getContent());
									icon = GLOBAL.processing.loadImage(next.getContent());
								}
							}
						}
					}
					catch(Exception e) {
					}

					additionalInfo = new ArtistInfo(begin, end, comment, aliases, country, gender, tags,icon);

					if (artistType.equalsIgnoreCase("group")) {
						Group g = new Group(arid, arName,additionalInfo);
						//GLOBAL.artistsSelected.add(g);
						GLOBAL.groupsSelected.add(g);
						GLOBAL.groupsInfoButtonSelected.add(new ButtonPlusMinusArtist(g.arName, g));
						setUserBreakdown(g);
						setAffineArtists(g);
						//System.out.println("New group added: "+g.arName);
						if(GLOBAL.affineArtistsToShow.contains(g))
							GLOBAL.graphsArea.networkGraph.loadData();
					}
					else if (artistType.equalsIgnoreCase("person")) {
						Person p = new Person(arid, arName, additionalInfo);
						//GLOBAL.artistsSelected.add(p);
						GLOBAL.personsSelected.add(p);
						GLOBAL.personsInfoButtonSelected.add(new ButtonPlusMinusArtist(p.arName, p));
						setUserBreakdown(p);
						setAffineArtists(p);
						//System.out.println("New person added: "+p.arName);
						if(GLOBAL.affineArtistsToShow.contains(p))
							GLOBAL.graphsArea.networkGraph.loadData();
					}
					else {
						Artist a = new Artist(arid, arName, additionalInfo);
						GLOBAL.artistsSelected.add(a);
						GLOBAL.artistsInfoButtonSelected.add(new ButtonPlusMinusArtist(a.arName, a));
						setUserBreakdown(a);
						setAffineArtists(a);
						//System.out.println("No class found for mapping type: "+artistType);
						if(GLOBAL.affineArtistsToShow.contains(a))
							GLOBAL.graphsArea.networkGraph.loadData();
					}

					// Decrement threads count
					GLOBAL.artistsThreadsActive--;

				} // xml != null
			}
			else{
				System.err.println("Arid null: artist not added.");
				// Decrement threads count
				GLOBAL.artistsThreadsActive--;
				return;
			}
			}catch(Exception e){
				// Decrement threads count
				GLOBAL.artistsThreadsActive--;
			}
		}

		// Set affine artists
		public void setAffineArtists(Artist a) {
			ArrayList<Artist> list = new ArrayList<Artist>();

			String url;
			XMLElement xml;

			try {

				// Parse all artists
				url = "http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid="+a.arid+
						"&api_key=43e01402e7c496a4563b540e46c7355a";
				xml = new XMLElement(GLOBAL.processing,url);

				if (xml.getChild("similarartists")!=null) {
					XMLElement el = xml.getChild("similarartists");
					int count = el.getChildCount();
					for (int i=0; i<count; i++) {
						String name, mbid;

						XMLElement next = el.getChild(i); // an artist

						name = next.getChild("name").getContent();
						mbid = next.getChild("mbid").getContent();
						//System.out.println("mbid: "+next.getChild("mbid").getContent());
						if (mbid == null)
							mbid = "";

						Artist lastfm_artist = new Artist(mbid, name);
						setUserBreakdown(lastfm_artist);
						list.add(lastfm_artist);		
					}
				}


				// Prepare arrays to be sorted
				float[] values = new float[list.size()];
				Artist[] artists = new Artist[list.size()];

				// Set distances and artists in the same indexes
				for (int i=0; i<list.size(); i++) {
					values[i] = calculateArtistDistances(a, list.get(i));
					artists[i] = list.get(i);
				}

				// Bubblesort
				boolean found = false;
				do {
					found = false;
					for (int i=0; i<artists.length-1; i++) {
						if (values[i] > values[i+1]) {
							float temp_values;
							Artist temp_artist;

							temp_values = values[i];
							values[i] = values[i+1];
							values[i+1] = temp_values;

							temp_artist = artists[i];
							artists[i] = artists[i+1];
							artists[i+1] = temp_artist;

							found = true;
						}
					}
				}while(found);


				ArrayList<Artist> new_list = new ArrayList<Artist>();
				// Save affine artists
				for (int i=0; i<artists.length; i++) {
					new_list.add(artists[i]);
				}			
				GLOBAL.affineArtists.put(a, new_list);

			}
			catch(Exception e) {
				System.err.println("Cannot find similar artists from lastfm");
			}

		}

		// Set the breakdown of the typical user type
		public void setUserBreakdown(Artist a) {
			try {
				a.user_nationalities = Parser.from_mbid_to_userBreakdown.get(arid).user_nationalities;
				a.user_ages_integers = Parser.from_mbid_to_userBreakdown.get(arid).user_ages_integers;
				a.user_sex = Parser.from_mbid_to_userBreakdown.get(arid).user_sex;
				a.user_ages_intervals = setIntervals(Parser.from_mbid_to_userBreakdown.get(arid).user_ages_integers);
			}
			catch(Exception e) {
				System.err.println("user breakdown creation problems");
			}
		}

		public ArrayList<String> setIntervals(ArrayList<Integer> ages) {
			ArrayList<String> intervals = new ArrayList<String>();

			boolean start_separator = false;
			String interval = "";

			Object[] a = ages.toArray();

			Arrays.sort(a);

			//			for(int i=0; i<a.length; i++)
			//				System.out.print((Integer)a[i]+" ");

			for (int i=0; i<a.length; i++) {
				if ( i< a.length-1) {
					if( (start_separator==false && (Integer)a[i+1] != (Integer)a[i] + 1)) {
						intervals.add(((Integer)a[i]).toString());
					}
					else if (start_separator == false && ((Integer)a[i+1] == (Integer)a[i] + 1)) {
						interval = ((Integer)a[i]).toString() + "-";
						start_separator = true;
					}
					else if (start_separator == true && ((Integer)a[i+1] == (Integer)a[i] + 1) ) {
						continue;
					}
					else if (start_separator == true && ((Integer)a[i+1] != (Integer)a[i] + 1) ) {
						interval = interval +((Integer)a[i]).toString();
						intervals.add(interval);
						interval = "";
						start_separator = false;
					}
				}
				else{
					interval = interval + ((Integer)a[i]).toString();
					intervals.add(interval);
				}
			}

			return intervals;
		}


	}


	public static class lastfmTopArtists extends Thread{

		public lastfmTopArtists() {}

		public void run() {
			String url;
			XMLElement xml_lastfm = null;
			// Search for the image
			try {
				url = "http://ws.audioscrobbler.com/2.0/?method=chart.gettopartists&api_key=43e01402e7c496a4563b540e46c7355a";
				xml_lastfm = new XMLElement(GLOBAL.processing,url);
				if (xml_lastfm.getChild("artists")!=null) {
					XMLElement el = xml_lastfm.getChild("artists");
					int count = el.getChildCount();
					for (int i=0; i<count; i++) {
						XMLElement next = el.getChild(i);
						GLOBAL.lastfm_TopArtistsNames.add(next.getChild("name").getContent());
						GLOBAL.lastfm_TopArtistsIDs.add(next.getChild("mbid").getContent());
						GLOBAL.lastfm_TopArtistsPlayCount.add(Integer.parseInt(next.getChild("playcount").getContent()));
						GLOBAL.lastfm_TopArtistsListeners.add(Integer.parseInt(next.getChild("listeners").getContent()));
						//						System.out.println(next.getChild("name").getContent()+" "+
						//								next.getChild("mbid").getContent()+" "+
						//								Integer.parseInt(next.getChild("listeners").getContent()));
					}
				}
				
				// Prepare arrays to be sorted
				String[] names = new String[GLOBAL.lastfm_TopArtistsPlayCount.size()];
				String[] ids = new String[GLOBAL.lastfm_TopArtistsPlayCount.size()];
				int[] playcount = new int [GLOBAL.lastfm_TopArtistsPlayCount.size()];
				int[] listeners = new int [GLOBAL.lastfm_TopArtistsPlayCount.size()];
				
				// fill
				for (int i=0; i<GLOBAL.lastfm_TopArtistsPlayCount.size();i++ ) {
					names[i] = GLOBAL.lastfm_TopArtistsNames.get(i);
					ids[i] = GLOBAL.lastfm_TopArtistsIDs.get(i);
					playcount[i] = GLOBAL.lastfm_TopArtistsPlayCount.get(i).intValue();
					listeners[i] = GLOBAL.lastfm_TopArtistsListeners.get(i).intValue();
				}
				
				// Bubblesort
				boolean found = false;
				do {
					found = false;
					for (int i=0; i<playcount.length-1; i++) {
						if (playcount[i] < playcount[i+1]) {
							
							int temp_playcount,temp_listeners;
							String temp_id, temp_name;

							temp_playcount = playcount[i];
							playcount[i] = playcount[i+1];
							playcount[i+1] = temp_playcount;

							temp_name = names[i];
							names[i] = names[i+1];
							names[i+1] = temp_name;
							
							temp_id = ids[i];
							ids[i] = ids[i+1];
							ids[i+1] = temp_id;
							
							temp_listeners = listeners[i];
							listeners[i] = listeners[i+1];
							listeners[i+1] = temp_listeners;

							found = true;
						}
					}
				}while(found);
				
				// Put back values
				GLOBAL.lastfm_TopArtistsIDs.clear();
				GLOBAL.lastfm_TopArtistsListeners.clear();
				GLOBAL.lastfm_TopArtistsNames.clear();
				GLOBAL.lastfm_TopArtistsPlayCount.clear();
				for(int i=0; i< playcount.length; i++) {
					GLOBAL.lastfm_TopArtistsIDs.add(ids[i]);
					GLOBAL.lastfm_TopArtistsNames.add(names[i]);
					GLOBAL.lastfm_TopArtistsPlayCount.add(playcount[i]);
					GLOBAL.lastfm_TopArtistsListeners.add(listeners[i]);
				}
				
				
			}
			catch(Exception e) {
				System.err.println("Download of top artists failed.");
			}
		}

	}





}
